<?php

namespace app\command\optimize;

use Exception;
use think\console\Command;
use think\console\Input;
use think\console\input\Argument;
use think\console\input\Option;
use think\console\Output;
use think\db\PDOConnection;

class Schema extends Command
{
	protected function configure()
	{
		$this->setName('optimize:schema')
			->addArgument('dir', Argument::OPTIONAL, '模型所在目录，例如app/default_model/aa则可以写成default_model/aa，不考虑系统目录分隔符，一律使用/')
			->addOption('connection', null, Option::VALUE_REQUIRED, '数据库连接配置名，在config/database中。仅当指定了--table时有效')
			->addOption('table', null, Option::VALUE_REQUIRED, '表名，支持通配符*，也可以用database.table指定数据库')
			->setDescription('构建/刷新模型缓存文件');
	}

	protected function execute(Input $input, Output $output)
	{
		if ($input->hasOption('table')) {
			$connection = $this->app->db->connect($input->getOption('connection'));
			$output->info('Schema cache for connection: ' . $connection->getConfig('hostname') . ' -> ' . $connection->getConfig('database'));
			if (!$connection instanceof PDOConnection) {
				$output->error("only PDO connection support schema cache!");
				return;
			}
			$table = $input->getOption('table');
			if (false === strpos($table, '.')) {
				$dbName = $connection->getConfig('database');
			} else {
				[$dbName, $table] = explode('.', $table);
			}

			if ($table == '*') { // [TODO] 这个情况还有报错
				$table = $connection->getTables($dbName);
			}

			$this->buildDataBaseSchema($connection, (array) $table, $dbName);
		} else {
			$appPath   = $this->app->getBasePath();
			$dir = $input->getArgument('dir') ?: 'model';

			$modelDir = str_replace('/', DIRECTORY_SEPARATOR, $appPath . $dir . DIRECTORY_SEPARATOR);
			$baseNamespace = 'app\\' . str_replace('/', '\\', $dir) . '\\';

			$allFiles = $this->scanDir($modelDir, true);
			if (count($allFiles) == 0) {
				dump('[empty] -- no files found in ' . $modelDir);
				return;
			}
			// halt($modelDir, $baseNamespace, $allFiles);

			foreach ($allFiles as $file) {
				if (pathinfo($file, PATHINFO_EXTENSION) !== 'php') {
					dump('[skip] -- not php file:' . $file);
					continue;
				}

				// halt(pathinfo('config\game_xxl\UserJoinCamp.php', PATHINFO_ALL));
				// $file = 'config/UserJoinCamp.php';
				$relativeDirname = pathinfo($file, PATHINFO_DIRNAME);
				if ($relativeDirname == '.') {
					$relativeDirname = '';
				} else {
					$relativeDirname .= '\\';
				}
				$className = $baseNamespace . $relativeDirname . pathinfo($file, PATHINFO_FILENAME);

				if (!class_exists($className)) {
					dump('[skip] -- class [' . $className . '] not exists, file [' . $file . ']');
					continue;
				}

				try {
					$this->buildModelSchema($className);
					dump('[success] -- class [' . $className . ']');
				} catch (\Exception $e) {
					$output->error('[error] -- class [' . $className . '], file [' . $className . '] build schema error: ' . $e->getMessage());
				}
			}
		}

		$output->info('Succeed!');
	}

	protected function scanDir($dir, $deep = true, $base = '')
	{
		$result = [];
		if (!is_dir($dir)) return $result;

		$items = scandir($dir);
		foreach ($items as $item) {
			if ($item === '.' || $item === '..') {
				continue;
			}

			$fullPath = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $item;
			$relativePath = ltrim($base . $item, DIRECTORY_SEPARATOR);

			if (is_dir($fullPath)) {
				if ($deep) {
					// 递归子目录
					$result = array_merge($result, $this->scanDir($fullPath, true, $relativePath . DIRECTORY_SEPARATOR));
				}
			} else {
				$result[] = $relativePath;
			}
		}

		return $result;
	}

	protected function buildModelSchema(string $class): void
	{
		$reflect = new \ReflectionClass($class);
		if ($reflect->isAbstract()) throw new \Exception("Class $class is abstract, cannot build schema.");
		if (!$reflect->isSubclassOf('\think\Model')) throw new \Exception("Class $class is not a subclass of think\Model, cannot build schema.");

		/** @var \think\Model $model */
		$model      = new $class;
		$connection = $model->db()->getConnection();
		if ($connection instanceof PDOConnection) {
			$table = $model->getTable();
			//预读字段信息
			$connection->getSchemaInfo($table, true);
		}
	}

	protected function buildDataBaseSchema(PDOConnection $connection, array $tables, string $dbName): void
	{
		foreach ($tables as $table) {
			//预读字段信息
			$connection->getSchemaInfo("{$dbName}.{$table}", true);
			dump('[success] -- table [' . $table . ']');
		}
	}
}
